﻿using Cofe.Core.Interfaces;
using Cofe.Core.Logger;
using Cofe.Core.Property;
using Cofe.Core.Security;
using Cofe.Core.Session;
using Cofe.Core;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Cofe.Core.Implements
{
    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class VolumeRegistrar : IVolumeRegistrar, ISaveSettings
    {
        #region Constructor

        public VolumeRegistrar()
        {
        }


        #endregion

        #region Methods

        protected virtual IVolume registerVolume(IVolumeFactory factory, IVolume volume)
        {
            lock (_registeredVolumes)

                if (factory is ISystemVolumeFactory)
                {
                    if (_registeredSystemVolumes.ContainsKey(volume.VolumeId))
                        _registeredSystemVolumes[volume.VolumeId] = volume;
                    else _registeredSystemVolumes.Add(volume.VolumeId, volume);
                }
                else
                {
                    if (_registeredVolumes.ContainsKey(volume.VolumeId))
                        _registeredVolumes[volume.VolumeId] = volume;
                    else _registeredVolumes.Add(volume.VolumeId, volume);
                }

            return volume;
        }

        protected virtual void removeVolume(string volumeId)
        {
            lock (_registeredVolumes)
                if (_registeredVolumes.ContainsKey(volumeId))
                {
                    var vol = _registeredVolumes[volumeId];
                    _registeredVolumes.Remove(volumeId);
                    vol.Dispose();
                }
        }

        public async Task<IVolume> RegisterVolumeAsync(IVolumeInfo volInfo)
        {
            using (var v = CofeServices.SecurityManager.ImpersonateAsService("VolumeRegistrar"))
            {
                var factory = VolumeFactories.FirstOrDefault((f) => f.FactoryId == volInfo.FactoryId);
                if (factory == null)
                    throw new KeyNotFoundException(volInfo.FactoryId + " is not found.");
                var volume = await factory.ConstructAsync(this, volInfo.VolumeId, volInfo.ParamString);
                foreach (var es in volume.EventSources)
                    CofeServices.EventHub.RegisterEventSource(es);
                var registeredVolume = registerVolume(factory, volume);
                if (!(factory is ISystemVolumeFactory))
                    this.Behaviors.NotifyAdded(registeredVolume);
                return registeredVolume;
            }

        }

        public IVolume RegisterVolume(IVolumeInfo volInfo)
        {
            return Task.Run<IVolume>(async () => await RegisterVolumeAsync(volInfo)).Result;
        }

        public void RemoveVolume(string volumeId)
        {
            var vol = LookupVolume(volumeId);
            if (vol != null)
            {
                removeVolume(volumeId);
                this.Behaviors.NotifyRemoved(vol);
            }
        }

        public IVolume LookupVolume(string volumeId)
        {
            lock (_registeredVolumes)
            {
                var retVal = LookupVolumes((v) => v.VolumeId == volumeId, true).FirstOrDefault();
                return retVal;
            }
        }

        public IEnumerable<IVolume> LookupVolumes(Func<IVolume, bool> filterFunc, bool includeSystem = false)
        {
            lock (_registeredVolumes)
            {
                if (includeSystem)
                    foreach (var vol in _registeredSystemVolumes.Values)
                        if (filterFunc(vol))
                            yield return vol;

                foreach (var vol in _registeredVolumes.Values)
                    if (filterFunc(vol))
                        yield return vol;
            }
        }

        public Task SaveAsync(Session.ICofeSettings settings)
        {
            return Task.Run(() =>
                {
                    settings.Volumes = new VolumeInfoList(_registeredVolumes.Values.Select(
                        (v) => (IVolumeInfo)new VolumeInfo() { FactoryId = v.FactoryId, ParamString = v.ParamString, VolumeId = v.VolumeId }));
                });
        }

        public Task RestoreAsync(Session.ICofeSettings settings)
        {
            return Task.Run(() =>
                {
                    _registeredVolumes.Clear();
                    Task.WaitAll(
                        (from v in settings.Volumes
                         select RegisterVolumeAsync(v)).ToArray());
                });
        }

        #endregion

        #region Data

        Dictionary<string, IVolume> _registeredVolumes = new Dictionary<string, IVolume>();
        Dictionary<string, IVolume> _registeredSystemVolumes = new Dictionary<string, IVolume>();
        private IVolumeFactory[] _volumeFactories = null;
        private IVolumeRegistrarBehaviorManager _behaviors = null;

        #endregion

        #region Public Properties

        public IVolumeRegistrarBehaviorManager Behaviors
        {
            get
            {
                return _behaviors ?? (_behaviors = CofeServices.ServiceLocater.FindService<IVolumeRegistrarBehaviorManager>());
            }
            set { _behaviors = value; }
        }
        public IVolumeFactory[] VolumeFactories
        {
            get { return _volumeFactories ?? CofeServices.ServiceLocater.FindAllServices<IVolumeFactory>().ToArray(); }
            set { _volumeFactories = value; }
        }


        #endregion

    }
}
